package com.stone.design.mode.pipeline.netty.context;

import com.stone.design.mode.pipeline.netty.handler.ChannelHandler;
import com.stone.design.mode.pipeline.netty.handler.in.ChannelInboundHandler;
import com.stone.design.mode.pipeline.netty.handler.out.ChannelOutboundHandler;
import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.Map;
import java.util.WeakHashMap;

/**
 * channelHandler的掩码信息
 *
 * @author wenpan 2024/01/21 17:29
 */
@Slf4j
public class ChannelHandlerMask {
    // ========================== inbound事件相关掩码 ==========================
    /**
     * Using to mask which methods must be called for a ChannelHandler.
     */
    static final int MASK_EXCEPTION_CAUGHT = 1;
    /**
     * channelRegistered 方法掩码（channel注册到reactor上）
     */
    static final int MASK_CHANNEL_REGISTERED = 1 << 1;
    /**
     * channelUnregistered 方法（channel从reactor上取消注册）
     */
    static final int MASK_CHANNEL_UNREGISTERED = 1 << 2;
    /**
     * channelActive 方法
     */
    public static final int MASK_CHANNEL_ACTIVE = 1 << 3;

    // ========================== outbound事件相关掩码 ==========================
    /**
     * write 方法掩码
     */
    static final int MASK_WRITE = 1 << 15;
    /**
     * flush 方法掩码
     */
    static final int MASK_FLUSH = 1 << 16;

    /**
     * inbound事件的掩码集合（代表和入站相关的方法）
     */
    public static final int MASK_ONLY_INBOUND = MASK_CHANNEL_REGISTERED |
            MASK_CHANNEL_UNREGISTERED | MASK_CHANNEL_ACTIVE;
    /**
     * 所有inbound事件的掩码集合
     */
    private static final int MASK_ALL_INBOUND = MASK_EXCEPTION_CAUGHT | MASK_ONLY_INBOUND;
    /**
     * outbound事件掩码集合（代表和出站相关的方法）
     */
    static final int MASK_ONLY_OUTBOUND = MASK_WRITE | MASK_FLUSH;
    /**
     * 所有outbound事件掩码集合
     */
    private static final int MASK_ALL_OUTBOUND = MASK_EXCEPTION_CAUGHT | MASK_ONLY_OUTBOUND;

    // handler 掩码缓存，以handler的Clazz为key，该Class的掩码为value
    // 这里需要一个 FastThreadLocal 类型的 MASKS 字段来缓存 ChannelHandler 对应的执行掩码。因为 ChannelHandler 类一旦被定义出来它的执行掩码就固定了，
    // 而 netty 需要接收大量的连接，创建大量的 channel ，并为这些 channel 初始化对应的 pipeline ，需要频繁的记录 channelHandler 的执行掩码到 context 类中，
    // 所以这里需要将掩码缓存起来
    private static final ThreadLocal<Map<Class<? extends ChannelHandler>, Integer>> MASKS =
            new ThreadLocal<Map<Class<? extends ChannelHandler>, Integer>>() {
                @Override
                protected Map<Class<? extends ChannelHandler>, Integer> initialValue() {
                    return new WeakHashMap<Class<? extends ChannelHandler>, Integer>(32);
                }
            };

    /**
     * 计算 ChannelHandler 类的掩码（也就是实现了哪些方法）
     */
    public static int mask(Class<? extends ChannelHandler> clazz) {
        // 因为每建立一个channel就会初始化一个pipeline，这里需要将ChannelHandler对应的mask缓存
        Map<Class<? extends ChannelHandler>, Integer> cache = MASKS.get();
        Integer mask = cache.get(clazz);
        // 如果缓存中还没有则计算一次并缓存
        if (mask == null) {
            // 计算ChannelHandler对应的mask（什么类型的ChannelHandler，对什么事件感兴趣）
            mask = mask0(clazz);
            cache.put(clazz, mask);
        }
        return mask;
    }

    /**
     * 计算 handlerType Class的掩码
     * 从这个过程中我们可以看到，ChannelHandler 的执行掩码包含的是该 ChannelHandler 感兴趣的事件掩码集合。当事件在 pipeline 中传播的时候
     * ，在 ChannelHandlerContext 中可以利用这个执行掩码来判断，当前 ChannelHandler 是否符合响应该事件的资格。
     */
    private static int mask0(Class<? extends ChannelHandler> handlerType) {
        int mask = MASK_EXCEPTION_CAUGHT;
        try {
            // 入站处理器handler
            if (ChannelInboundHandler.class.isAssignableFrom(handlerType)) {
                // 使用或运算将所有的入站事件都添加到mask掩码里
                mask |= MASK_ALL_INBOUND;

                // ============================下面的这些方法都是检查某些方法使用要从掩码里去除============================

                //最后在对不感兴趣的事件一一排除（handler中的事件回调方法如果标注了@Skip注解，则认为handler对该事件不感兴趣）
                // 检查handlerType是否需要跳过channelRegistered方法，如果为true则从mask里去除该方法
                if (isSkippable(handlerType, "channelRegistered", ChannelHandlerContext.class)) {
                    // 取反再做与运算，就相当于将 channelRegistered 方法从掩码中去除了
                    mask &= ~MASK_CHANNEL_REGISTERED;
                }
                if (isSkippable(handlerType, "channelUnregistered", ChannelHandlerContext.class)) {
                    mask &= ~MASK_CHANNEL_UNREGISTERED;
                }
                if (isSkippable(handlerType, "channelActive", ChannelHandlerContext.class)) {
                    mask &= ~MASK_CHANNEL_ACTIVE;
                }
            }

            // 出站handler
            if (ChannelOutboundHandler.class.isAssignableFrom(handlerType)) {
                //如果handler为Outbound类型的，则先将全部outbound事件设置进掩码中
                mask |= MASK_ALL_OUTBOUND;

                if (isSkippable(handlerType, "write", ChannelHandlerContext.class, Object.class)) {
                    mask &= ~MASK_WRITE;
                }
                if (isSkippable(handlerType, "flush", ChannelHandlerContext.class)) {
                    mask &= ~MASK_FLUSH;
                }
            }

            if (isSkippable(handlerType, "exceptionCaught", ChannelHandlerContext.class, Throwable.class)) {
                mask &= ~MASK_EXCEPTION_CAUGHT;
            }
        } catch (Exception e) {
            // Should never reach here.
        }

        // 计算出的掩码需要缓存，因为每次向pipeline中添加该类型的handler的时候都需要获取掩码（创建一个channel 就需要为其初始化pipeline）
        return mask;
    }

    @SuppressWarnings("rawtypes")
    private static boolean isSkippable(Class<?> handlerType,
                                       String methodName,
                                       Class<?>... paramTypes) throws Exception {
        return AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {
            @Override
            public Boolean run() throws Exception {
                Method m;
                try {
                    // 从 handlerType clazz里根据方法名称和参数取出method
                    m = handlerType.getMethod(methodName, paramTypes);
                } catch (NoSuchMethodException e) {
                    if (log.isDebugEnabled()) {
                        log.debug(
                                "Class {} missing method {}, assume we can not skip execution", handlerType, methodName, e);
                    }
                    return false;
                }
                // 方法上有标注skip注解，则认为需要跳过
                return m != null && m.isAnnotationPresent(Skip.class);
            }
        });
    }

    private ChannelHandlerMask() {
    }

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface Skip {
        // no value
    }

}
